#include "process.h"
#include "simulator.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


double averageCompletionTime;
int totalProcesses;
long minCompletionTime;
long maxCompletionTime;
int firstProcess;
long usefulTime;

/* By Jacob Eichers and Cliff Buchanan. Observes the flow of program and
	prints out statistics at the end of the program. Also contains optional
	debug message outputs*/

/* Initalizes the observer. Should be called before
	any obs_ methods are used.*/
void obs_init()
{
	averageCompletionTime = 0;
	totalProcesses = 0;
	minCompletionTime = 0;
	maxCompletionTime = 0;
	firstProcess = 1;
	usefulTime = 0;
}


/* Print cumulative statistics for the simulator.*/
void obs_print_stats()
{
	const float sinms = 1/1000.0;

	printf("ACT: \t\t\t%f\n", sinms*averageCompletionTime);
	printf("min completion time: \t%f\n", sinms*minCompletionTime);
	printf("max completion time: \t%f\n", sinms*maxCompletionTime);
	printf("Throughput: \t\t%f\n", totalProcesses/(sim_get_clock()*sinms));
	printf("Utilization: \t\t%f\n", (0.0+usefulTime)/sim_get_clock());
}

/* call when a job is added to the scheduler*/
void obs_sched_add(struct process *p)
{
	if(DEBUG)
	{
		printf("%ims: %s added to sched\t\t\t",
			sim_get_clock(), p->name);
		proc_print(p);
	}
}

/* call when a job is created by tread*/
void obs_tread_next(struct process *p)
{
	if(DEBUG)
	{
		printf("%ims: %s spawned\t\t\t",
			sim_get_clock(), p->name);
		proc_print(p);
	}
}

/* call when a process exits*/
void obs_proc_exit(struct process *p)
{
	int runTime;
	usefulTime += p->lifeTime;
	runTime = sim_get_clock() - (p->arrivalTime);

	if(totalProcesses==0)
		{averageCompletionTime = runTime;}
	else
	{
		averageCompletionTime =
			(averageCompletionTime*totalProcesses+runTime)/
			(totalProcesses+1.0);
	}
	totalProcesses++;

	if (firstProcess)
	{
		minCompletionTime = runTime;
		firstProcess = 0;
	}
	if (runTime > maxCompletionTime)
		{ maxCompletionTime = runTime;}
	if (runTime < minCompletionTime)
		{ minCompletionTime = runTime;}

	

	if(DEBUG)
	{
		printf("%ims: %s exited\t\t\t",
			sim_get_clock(), p->name);
		proc_print(p);
	}
}

/* call when proc_run is called*/
void obs_proc_ran(struct process *p, int t)
{
	if(DEBUG)
	{
		printf("%ims: %s ran for %ims\t\t\t",
			sim_get_clock(), p->name, t);
		proc_print(p);
	}
}

/* call when proc_did_io is called*/
void obs_proc_did_io(struct process *p, int blocks)
{
	if(DEBUG)
	{
		printf("%ims: %s moved %iblocks\t\t\t",
			sim_get_clock(), p->name, blocks);
		proc_print(p);
	}
}

/* call when the disk accepts a job*/
void obs_disk_accept_job(struct process *p)
{
	if(DEBUG)
	{
		printf("%ims: disk accepted %s\t\t\t",
			sim_get_clock(), p->name);
		proc_print(p);
	}
}

/* call when the disk accepts a job*/
void obs_disk_return_job(struct process *p)
{
	if(DEBUG)
	{
		printf("%ims: disk returned %s\t\t\t",
			sim_get_clock(), p->name);
		proc_print(p);
	}
}

/* call when sim_run_clock() is called*/
void obs_sim_run_clock(int t)
{
}

/* call when sim_set_context is called*/
void obs_sim_set_context(struct process *p)
{
	if(DEBUG)
	{
		printf("%ims: sim swapped to %s\n",
			sim_get_clock(), p ? p->name : "NULL");
	}
}
